package main

import (
	"container/list"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"
)

var s_all_url *list.List
var chls []chan bool

// example:
// Html like: <a href="http://www.baidu.com/1.html"
// Html_prefix like: < a href="
// Image like: src="/image/1.jpg"
// Image_prefix like: src="
// Image_check like: *.jpg
// State like: http://www.baidu.com/
// SrcUrl like: http://www.baidu.com/1.html
type RegConfig struct {
	Html         string // html url regex in the html body
	Html_prefix  string // to be replace to empty in the html url
	Image        string // image url in the html body
	Image_prefix string // to be replace to empty in the image url
	Image_check  string // check if the image url ok
	State        string // the state of the website
	SrcUrl       string // the url to down pics
}

var manconfig RegConfig

func checkUrl(str string) bool {
	re, err := regexp.Compile(manconfig.Image_check)
	if err != nil {
		fmt.Println(err)
		return false
	}
	if !re.MatchString(str) {
		return false
	}
	for e := s_all_url.Front(); e != nil; e = e.Next() {
		if e.Value.(string) == str {
			return false
		}
	}
	s_all_url.PushBack(str)
	return true
}

// this function get here:http://www.dotcoo.com/golang-substr-char
// all right of this function belong the author
func Substr(str string, start, length int) string {
	rs := []rune(str)
	rl := len(rs)
	end := 0

	if start < 0 {
		start = rl - 1 + start
	}
	end = start + length

	if start > end {
		start, end = end, start
	}

	if start < 0 {
		start = 0
	}
	if start > rl {
		start = rl
	}
	if end < 0 {
		end = 0
	}
	if end > rl {
		end = rl
	}

	return string(rs[start:end])
}

func getUrl(url string) (body []byte, err error) {
	fmt.Println("get url in")
	resp, err := http.Get(url)
	fmt.Println("get url in 2")
	if err != nil {
		fmt.Println(err)
		return body, err
	}
	defer resp.Body.Close()
	// FIXME: it seems cost most time here?
	body, err = ioutil.ReadAll(resp.Body)
	fmt.Println("get url in 3")
	if err != nil {
		fmt.Println(err)
		return body, err
	}
	return body, err
}

func checkDir(path string) {
	fi, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			fmt.Println(err.Error())
		}
	} else {
		if fi.IsDir() {
			return
		}
	}
	os.Mkdir(path, 644)
}

func downPic(url string) {
	if !checkUrl(url) {
		return
	}
	fmt.Println("down pic--checkUrl")
	index := strings.LastIndex(url, "/")
	name := Substr(url, index+1, len(url)-index-1)
	body, err := getUrl(url)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	path := "img"
	//	checkDir(path)
	name = path + "/" + name
	ioutil.WriteFile(name, body, 0777)

}

func getPicUrl(url string, i int) {
	defer func() { <-chls[i] }()
	fmt.Println("begin get pic url...")
	body, err := getUrl(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	re, err := regexp.Compile(manconfig.Image)
	if err != nil {
		fmt.Println(err)
		return
	}
	replace, err := regexp.Compile(manconfig.Image_prefix)
	if err != nil {
		fmt.Println(err)
	}
	result := re.FindAllSubmatch(body, -1)
	for _, col := range result {
		str_pic := replace.ReplaceAllString(string(col[0]), "")
		fmt.Println(str_pic)
		downPic(str_pic)
	}
}

func getparams(filename string) bool {
	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("error:" + err.Error())
	}
	decoder := json.NewDecoder(file)
	manconfig = RegConfig{}
	err = decoder.Decode(&manconfig)
	if err != nil {
		fmt.Println("error:" + err.Error())
		return false
	}
	fmt.Println(manconfig)
	return true
}

func main() {
	//	for i := 0; i != 30; i++ {
	//		Down(string(i))
	//	}
	getparams("conf.json")
	Down(manconfig.SrcUrl)
}
func Down(htmlurl string) {

	s_all_url = list.New()
	// the base_url and state_url maybe your self url
	body, err := getUrl(htmlurl)
	if err != nil {
		fmt.Println(err)
		return
	}

	replace, err := regexp.Compile(manconfig.Html_prefix)
	re, err := regexp.Compile(manconfig.Html)

	if err != nil {
		panic(err)
	}
	result := re.FindAllSubmatch(body, -1)

	url_list := list.New()
	for _, str := range result {
		str2 := replace.ReplaceAllString(string(str[0]), "")
		str2 = manconfig.State + str2
		url_list.PushBack(str2)
	}
	chan_size := url_list.Len()

	for i := 0; i != chan_size; i++ {
		chls = append(chls, make(chan bool))
	}
	i := 0
	for iter := url_list.Front(); iter != nil; iter = iter.Next() {

		str := iter.Value.(string)
		if str == "" {
			fmt.Println("empty str")
			continue
		}
		fmt.Println("pic url is " + str)
		go getPicUrl(str, i)
		i++

	}
	for i := 0; i != len(chls); i++ {
		chls[i] <- true
	}

}
